/**
 * Copyright (C) 2010 DustedPixels.com
 */
package com.dustedpixels.cirt.model.cpu.z80;

import com.dustedpixels.cirt.model.core.Circuit;
import com.dustedpixels.cirt.model.core.In;
import com.dustedpixels.cirt.model.core.Internal;
import com.dustedpixels.cirt.model.core.Out;
import com.dustedpixels.cirt.model.core.Pins;
import com.dustedpixels.cirt.model.core.Script;
import com.dustedpixels.cirt.model.core.TriState;

/**
 * Z80
 *
 * @author Michal Pociecha-Los (micapolos@gmail.com)
 */
@Circuit
public final class Z80 {
  @Out public boolean addressActive;
  @Out @Pins(16) public int address;

  @Out public boolean dataActive;
  @In @Out @Pins(8) public int data;
  
  @Out @TriState public boolean mreq;
  @Out @TriState public boolean iorq;
  @Out @TriState public boolean rd;
  @Out @TriState public boolean wr;

  @In public boolean busReq;
  @Out public boolean busAck;

  @Out public boolean halt;
  @In public boolean irq;
  @In public boolean nmi;
  @Out public boolean m1;
  @In public boolean reset;
  @Out public boolean rfsh;
  @In public boolean wait;
  
  @Internal public final MachineCycleController machineCycleController = new MachineCycleController();
  @Internal public final InstructionDecoder instructionDecoder = new InstructionDecoder();
  
  @Internal public final Regs regs = new Regs();
  @Internal public final Reg8 opcodeReg = new Reg8();
  @Internal public final Reg16 irReg = new Reg16();
  @Internal public final Reg16 incDecReg = new Reg16();
  @Internal public final OffsetDecoder offsetDecoder = new OffsetDecoder();
  @Internal public final IncDec16 incDec16 = new IncDec16();
  @Internal public final Alu alu = new Alu();

  public void clkRise() {
    machineCycleController.dataIn = data;
    machineCycleController.clkRise();
  }
  
  public void clkFall() {
    machineCycleController.dataIn = data;
    machineCycleController.wait = wait;
    machineCycleController.clkFall();
    machineCycleController.update();
    
    if (machineCycleController.duringFetchCycle) {
      switch (machineCycleController.tCycle) {
      case 0:
        // TODO(micapolos): Latch address-bus to inc register
        break;
      case 1:
        // TODO(micapolos): Increment PC
        // TODO(micapolos): Execute micro-instruction
        break;
      case 2:
        break;
      case 3:
        // TODO(micapolos): Execute micro-instruction
        break;
      }
    } else {
      switch (machineCycleController.tCycle) {
      case 0:
        break;
      case 1:
        break;
      case 2:
        // TODO(micapolos): Execute micro-instruction
        break;
      }
    }
  }
  
  @Script
  public void update() {
    machineCycleController.update();
    
    addressActive = machineCycleController.addressOutActive;
    if (addressActive) {
      address = machineCycleController.addressOut;
    }
    
    dataActive = machineCycleController.dataOutActive;
    if (dataActive) {
      data = machineCycleController.dataOut;
    }
    
    mreq = machineCycleController.mreq;
    iorq = machineCycleController.iorq;
    rd = machineCycleController.rd;
    wr = machineCycleController.wr;
    m1 = machineCycleController.m1;
    rfsh = machineCycleController.rfsh;
  }
}
